Skip to content

feat(client): sign presigned URLs against a public endpoint#109

Merged
cjimti merged 1 commit into
mainfrom
feat/presign-public-endpoint
Jun 8, 2026
Merged

feat(client): sign presigned URLs against a public endpoint#109
cjimti merged 1 commit into
mainfrom
feat/presign-public-endpoint

Conversation

@cjimti

@cjimti cjimti commented Jun 8, 2026

Copy link
Copy Markdown
Member

Problem

Client builds its presign client from the same s3.Client used for data operations, so presigned URLs are signed against Config.Endpoint. In clustered deployments Endpoint is an internal/cluster address (for example a SeaweedFS service DNS name like http://seaweedfs-s3:8333). Presigned URLs handed back to a browser or an external agent therefore point at a host that is not resolvable or reachable from outside the cluster, so the download fails.

Change

Add an optional public-facing endpoint used only when signing presigned URLs:

  • Config.PresignEndpoint (env S3_PRESIGN_ENDPOINT) in pkg/client/config.go. When set, presigned URLs sign against it; when empty it falls back to Endpoint, preserving prior behavior. Added to FromEnv, Clone, and a PresignBaseEndpoint() helper.
  • In New (pkg/client/client.go), when PresignEndpoint is set, build a dedicated presign client whose BaseEndpoint is the public endpoint (carrying UsePathStyle over). Data operations keep using the internal-Endpoint client. With no presign endpoint configured, both are the same client, so behavior is unchanged.

This is safe for path-style addressing because the SigV4 query signature on a presigned URL does not cover the host, so swapping the base endpoint on the presign client still yields a signature that validates against the public host.

Stdlib vulnerability fix

make verify (govulncheck) flagged two reachable Go standard-library vulnerabilities because the build floated to the unpatched go1.26.3 toolchain (go.mod had no toolchain directive):

  • GO-2026-5039net/textproto, reachable via Client.GetObject -> io.ReadAll
  • GO-2026-5037crypto/x509, reachable via the same path and main

Pinned toolchain go1.26.4 (the patched release) in go.mod. actions/setup-go honors it via go-version-file: go.mod, and CI now installs the patched toolchain. govulncheck reports 0 reachable vulnerabilities.

Tests

TestNew_PresignEndpoint (pkg/client/client_test.go) builds a real client via New (presigning is local, no network) and asserts:

  • with PresignEndpoint set, the signed URL host is the public endpoint;
  • with it empty, the host falls back to Endpoint.

Verified adversarially: reverting the client.go change makes the test fail with the internal host (http://internal:8333) instead of the public host.

make verify passes end to end (tidy, lint, test, coverage, security, deadcode, build-check).

Downstream

Enables the consuming platform to hand out externally reachable presigned URLs by setting S3_PRESIGN_ENDPOINT / PresignEndpoint to the public S3 address while keeping the internal endpoint for data traffic. The platform-side wiring (a public_endpoint config mapped to PresignEndpoint) lands once this is released.

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.08%. Comparing base (4c809da) to head (321746c).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #109      +/-   ##
==========================================
+ Coverage   87.03%   87.08%   +0.04%     
==========================================
  Files          39       39              
  Lines        2167     2175       +8     
==========================================
+ Hits         1886     1894       +8     
  Misses        173      173              
  Partials      108      108              
Files with missing lines Coverage Δ
pkg/client/client.go 95.61% <100.00%> (+0.11%) ⬆️
pkg/client/config.go 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Presigned URLs were signed against Endpoint, which in clustered
deployments is an internal address unreachable from outside the
cluster. Add an optional PresignEndpoint (S3_PRESIGN_ENDPOINT) that,
when set, builds a dedicated presign client signing against the
public-facing host while all data operations keep using Endpoint.
Empty PresignEndpoint preserves prior behavior.

For path-style addressing the SigV4 query signature does not cover the
host, so swapping the base endpoint on the presign client yields a
valid signature against the public host.

Also pin toolchain go1.26.4 so the build uses the patched standard
library, clearing reachable govulncheck findings GO-2026-5039
(net/textproto) and GO-2026-5037 (crypto/x509).

TestNew_PresignEndpoint asserts the signed URL host is the public
endpoint when set and falls back to Endpoint when empty.
@cjimti cjimti force-pushed the feat/presign-public-endpoint branch from e11697a to 321746c Compare June 8, 2026 21:53
@cjimti cjimti merged commit 872fa60 into main Jun 8, 2026
6 checks passed
@cjimti cjimti deleted the feat/presign-public-endpoint branch June 8, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant